home *** CD-ROM | disk | FTP | other *** search
- //
- // MAILMRGE.C
- // Pegasus Mail for Windows extension providing a simple
- // "mail merge" capability.
- //
- // Copyright (c) 1994-95, David Harris, All Rights Reserved.
- //
- // The author grants explicit permission for this source code to be
- // used or modified as required, subject only to the conditions that
- // the copyright notices above are preserved and that by using this
- // code you agree that the code is provided without warranty of any
- // kind, either explicit or implied, and you use it at your own
- // risk.
- //
-
- // If you are using Borland C v4.0 or earlier, uncomment the next
- // line to prevent the Win32 porting layer code from being used.
- // #define NOPORTLAYER
-
- #define STRICT
- #include <windows.h>
-
- #ifndef NOPORTLAYER
- #include <windowsx.h>
- #endif
-
- #include <bwcc.h>
- #include <stdio.h>
- #include <string.h>
- #include <io.h> // for "access()"
- #include "..\wpmforms.h"
- #include "mailmrge.h"
-
- int atoi (const char *buf);
-
- #ifndef MAXFPATH
- #define MAXFPATH 128
- #endif
-
- char helpname [MAXFPATH];
- char help_used = 0;
-
- #define MAXFIELDS 32 // Maximum number of fields per record
-
- char szFormDlgClassName [] = "bordlg_mrg";
- char do_logging = 1;
- HFONT hLogFont;
- HWND last_focus;
-
- int register_form_classes (void);
-
- HINSTANCE hLibInstance; // set in LibMain, used throughout the DLL
-
-
- #pragma warn -par
-
-
- BOOL FAR PASCAL _export generic_proc (HWND hDialog, UINT wMsg,
- WPARAM wParam, LPARAM lParam)
- {
- // General purpose dialog procedure - simply exits as soon
- // as any button is pressed returning the ID of the button.
-
- BOOL fProcessed = TRUE;
-
- switch (wMsg)
- {
- case WM_INITDIALOG :
- // centre_window (hDialog);
- break;
-
- case WM_COMMAND :
- #ifdef NOPORTLAYER
- if (HIWORD (lParam) != BN_CLICKED) break;
- EndDialog (hDialog, wParam);
- #else
- if (GET_WM_COMMAND_CMD(wParam, lParam) != BN_CLICKED) break;
- EndDialog (hDialog, GET_WM_COMMAND_ID(wParam, lParam));
- #endif
- break;
-
- default:
- fProcessed = FALSE;
- break;
- }
-
- return fProcessed;
- }
-
-
- static void report_error (char *dlg_name)
- {
- FARPROC iproc;
-
- iproc = MakeProcInstance ((FARPROC) generic_proc, hLibInstance);
- DialogBox (hLibInstance, dlg_name, NULL, (DLGPROC) iproc);
- FreeProcInstance (iproc);
- }
-
-
- WORD FAR PASCAL _export FORMINIT (WORD version, int variant, HWND hParent,
- char *data, HWND *hDialog, char *callback_name)
- {
- // First, check to see if the version of the form manager is
- // one we can work with. This check is pretty arbitrary - you
- // should probably assume that you may not work with any version
- // where the major version number is higher than the original
- // version you targeted.
-
- if (version < 0x102)
- report_error ("VER");
-
- // Now check the variant number; for this extension, we only
- // provide a COMPOSER format.
-
- if (variant != 0) return 0;
-
- (*hDialog) = CreateDialog (hLibInstance, (LPCSTR) "MERGE", hParent, NULL);
- if ((*hDialog) == NULL) return 0;
- return 1;
- }
-
-
- void trim_newline (char *str)
- {
- /* Remove the terminating newline from a string
- ** if it's present. */
-
- int i;
-
- i = strlen (str) - 1;
- while ((i >= 0) && ((str [i] == '\n') || (str [i] == '\r')))
- str [i -- ] = '\0';
- }
-
-
- static int dump_the_data (FILE *dest, FILE *src, char **fields, int nfields)
- {
- // Write the formatted data into the temporary file we've created
- // for the message. Fields are inserted into the message by using
- // placeholders in the message file: a placeholder consists of a
- // tilde character (~), followed by a string of characters, ending
- // with another tilde. The following placeholders are recognized:
- //
- // "1" .. "32" - replaced by the matching field from the data record
- // "~" - replaced by a single tilde
-
- int c, state, field;
-
- rewind (src);
- state = 0;
- while ((c = fgetc (src)) != EOF)
- {
- switch (state)
- {
- case 0 : // Normal state
- if (c == '~')
- state = 1;
- else
- fputc (c, dest);
- break;
-
- case 1 : // Checking a placeholder
- if (c == '~')
- {
- fputc ('~', dest);
- state = 0;
- }
- else
- {
- if (strchr ("01234567890", c) != NULL)
- {
- field = c - '0';
- state = 2;
- }
- else
- {
- fputc ('~', dest);
- fputc (c, dest);
- state = 0;
- }
- }
- break;
-
- case 2 :
- if (c == '~')
- {
- if ((field > 0) && (field <= nfields))
- fprintf (dest, "%s", fields [field - 1]);
- state = 0;
- }
- else if (strchr ("01234567890", c) != NULL)
- {
- field *= 10;
- field += (c - '0');
- }
- else
- {
- fputc ('~', dest);
- fputc (c, dest);
- state = 0;
- }
- break;
- }
- }
-
- return 1;
- }
-
-
- static int mailmerge (HWND hWnd)
- {
- // Perform the actual mail merge:
- //
- // * Retrieve and validate the parameters in the dialog
- // * Open the data and format files
- // * Read the data one line at a time and parse it
- // * Generate a message per record using the format file.
- //
- // Returns: 1 on success
- // 0 on failure
-
- char buffer [1024], dfname [MAXFPATH],
- ffname [MAXFPATH], mfname [MAXFPATH], seps [80];
- char *fields [MAXFIELDS], *s;
- FILE *dfil, *ffil, *mfil;
- HWND hParent = GetParent (hWnd);
- int email_field, i;
-
- // First, check to see that the user has actually defined
- // a field separator of some kind.
-
- if (IsDlgButtonChecked (hWnd, IDC_TAB))
- {
- seps [0] = '\t';
- seps [1] = '\0';
- }
- else
- GetDlgItemText (hWnd, IDC_OTHEREDIT, seps, sizeof (seps));
-
- if (seps [0] == '\0')
- {
- report_error ("SEPS");
- return 0;
- }
-
- // Now get the data filename and the format filename,
- // verify that they exist, then open them.
-
- GetDlgItemText (hWnd, IDC_DATAFILE, dfname, sizeof (dfname));
- if (access (dfname, 0) != 0)
- {
- report_error ("NSDF");
- return 0;
- }
-
- GetDlgItemText (hWnd, IDC_FORMATFILE, ffname, sizeof (ffname));
- if (access (ffname, 0) != 0)
- {
- report_error ("NSFF");
- return 0;
- }
-
- if ((dfil = fopen (dfname, "rt")) == NULL)
- {
- report_error ("NSDF");
- return 0;
- }
-
- if ((ffil = fopen (ffname, "rt")) == NULL)
- {
- fclose (dfil);
- report_error ("NSFF");
- return 0;
- }
-
- // Now tell the Extension Manager to create a message for us...
-
- if (SendMessage (hParent, WM_F_NEWMESSAGE, 0, 0) == 0)
- {
- report_error ("MSGF");
- return 0;
- }
-
- // ... and fill out the basic fields in the message.
-
- GetDlgItemText (hWnd, IDC_SUBJECT, buffer, sizeof (buffer));
-
- SendMessage (hParent, WM_F_SUBJECT, 0, (LPARAM) buffer);
- if (IsDlgButtonChecked (hWnd, IDC_COPYSELF))
- SendMessage (hParent, WM_F_COPYSELF, 1, 0);
- if (IsDlgButtonChecked (hWnd, IDC_URGENT))
- SendMessage (hParent, WM_F_URGENT, 1, 0);
- if (IsDlgButtonChecked (hWnd, IDC_CONFIRMREADING))
- SendMessage (hParent, WM_F_CONFIRMREADING, 1, 0);
- if (IsDlgButtonChecked (hWnd, IDC_USEMIME))
- SendMessage (hParent, WM_F_MIME, 1, 0);
-
- GetDlgItemText (hWnd, IDC_EMAIL, buffer, sizeof (buffer));
- email_field = atoi (buffer);
- if (email_field < 1) email_field = 1;
- -- email_field;
-
- // Now we're down to business: we parse the data file one
- // line at a time, creating a temporary file for each message
- // we create, then send it.
-
- while (fgets (buffer, sizeof (buffer), dfil) != NULL)
- {
- trim_newline (buffer);
- if (buffer [0] == '\0') continue; // Blank line
- i = 1;
- s = buffer;
- fields [0] = buffer;
- while (*s)
- {
- if (strchr (seps, *s) != NULL)
- {
- *s = '\0';
- if (i < MAXFIELDS)
- fields [i ++] = s + 1;
- }
- ++ s;
- }
-
- fields [i] = NULL;
-
- SendMessage (hParent, WM_F_TEMPFILE, sizeof (mfname), (LPARAM) mfname);
- if ((mfil = fopen (mfname, "wt")) == NULL)
- {
- fclose (dfil);
- fclose (ffil);
- report_error ("TMPF");
- return 0;
- }
-
- dump_the_data (mfil, ffil, fields, i);
- fclose (mfil);
- SendMessage (hParent, WM_F_TO, 0, (LPARAM) (fields [email_field]));
- SendMessage (hParent, WM_F_MESSAGEFILE, 0, (LPARAM) mfname);
- SendMessage (hParent, WM_F_SENDMESSAGE, 0, 0);
- }
-
- fclose (dfil);
- fclose (ffil);
- return 1;
- }
-
-
- #pragma warn -use
-
- LONG FAR PASCAL _export MrgProc (HWND hWnd, UINT wMsg,
- WPARAM wParam, LPARAM lParam)
- {
- // Service routine for the form's enclosed dialog. This is a
- // standard windows modeless WndProc.
-
- DWORD dwResult = 0;
- BOOL fCallDefProc = TRUE;
- char buffer [256], *s, *to, *subj;
- RECT r;
-
- switch (wMsg)
- {
- case WM_FM_INIT :
- EnableWindow (GetDlgItem (hWnd, IDC_OTHEREDIT), FALSE);
- CheckRadioButton (hWnd, IDC_TAB, IDC_OTHER, IDC_TAB);
-
- // Now construct our help file name
- GetModuleFileName (hLibInstance, buffer, sizeof (buffer));
- s = strrchr (buffer, '.');
- if (s != NULL)
- {
- strcpy (s, ".HLP");
- strcpy (helpname, buffer);
- }
- break;
-
- case WM_DESTROY :
- // A good place to store some preferences, perhaps.
- if (help_used) WinHelp (hWnd, helpname, HELP_QUIT, 0);
- break;
-
- case WM_FM_INITFOCUS :
- SetFocus (GetDlgItem (hWnd, 101));
- break;
-
- case WM_FM_RESTOREFOCUS :
- // There's a glitch in the way the Windows MDI manager
- // seems to manage focus which means that we can have
- // focus taken away and not returned when the user does
- // anything outside our window. WinPMail has some quite
- // complex logic to deal with this case and will send
- // this message whenever focus should be restored in
- // our window. We set focus to the last active control
- // (which we know from trapping EN_SETFOCUS messages).
-
- if (last_focus) SetFocus (last_focus);
- break;
-
- case WM_COMMAND :
- fCallDefProc = FALSE;
-
- #ifdef NOPORTLAYER
- if (HIWORD(lParam) == EN_SETFOCUS)
- #else
- if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_SETFOCUS)
- #endif
- {
- // We have to trap EN_SETFOCUS messages so we know which
- // control was active last. When a menu selection is made
- // our current control will lose focus and because of a
- // curiosity in Windows' MDI management, we won't get it
- // back. Instead, WinPMail generates a WM_FM_RESTOREFOCUS
- // message which signals to us that we should set focus to
- // whatever the last active control was.
-
- #ifdef NOPORTLAYER
- last_focus = (HWND) LOWORD (lParam);
- #else
- last_focus = GET_WM_COMMAND_HWND(wParam, lParam);
- #endif
- break;
- }
-
- #ifdef NOPORTLAYER
- switch (wParam)
- #else
- switch (GET_WM_COMMAND_ID(wParam, lParam))
- #endif
- {
- case IDC_TAB :
- EnableWindow (GetDlgItem (hWnd, IDC_OTHEREDIT), FALSE);
- CheckRadioButton (hWnd, IDC_TAB, IDC_OTHER, IDC_TAB);
- break;
-
- case IDC_OTHER :
- EnableWindow (GetDlgItem (hWnd, IDC_OTHEREDIT), TRUE);
- CheckRadioButton (hWnd, IDC_TAB, IDC_OTHER, IDC_OTHER);
- break;
-
- case IDC_BROWSEDATAFILE :
- if (SendMessage (GetParent (hWnd), WM_F_BROWSEFILE, 1, (LPARAM) buffer))
- {
- SendDlgItemMessage (hWnd, IDC_DATAFILE, EM_SETSEL,
- 0, (LPARAM) 0x7FFF0000L);
- SendDlgItemMessage (hWnd, IDC_DATAFILE, EM_REPLACESEL,
- 0, (LPARAM) buffer);
- }
- break;
-
- case IDC_BROWSEFORMATFILE :
- if (SendMessage (GetParent (hWnd), WM_F_BROWSEFILE, 1, (LPARAM) buffer))
- {
- SendDlgItemMessage (hWnd, IDC_FORMATFILE, EM_SETSEL,
- 0, (LPARAM) 0x7FFF0000L);
- SendDlgItemMessage (hWnd, IDC_FORMATFILE, EM_REPLACESEL,
- 0, (LPARAM) buffer);
- }
- break;
-
- case IDC_HELP :
- if (helpname [0])
- {
- help_used = 1;
- WinHelp (hWnd, helpname, HELP_CONTEXT, 1);
- }
- else
- MessageBeep (0);
- break;
-
- case IDC_SEND :
- if (! mailmerge (hWnd)) break;
- // Drop through and close up shop...
-
- case IDC_CANCEL :
- PostMessage (GetParent (hWnd), WM_CLOSE, 0, 0);
- break;
- }
- break;
- }
-
- if (fCallDefProc)
- dwResult = BWCCDefDlgProc (hWnd, wMsg, wParam, lParam);
-
- return dwResult;
- }
-
-
- #pragma warn -sus
-
- void unregister_form_classes (void)
- {
- // Remove any classes associated with the form; we have the
- // same problem here as we do with registering the classes
- // for the DLL - we only want to deregister the classes on
- // the last time we're unloaded.
-
- if (GetModuleUsage (hLibInstance) > 1) return; // Not a problem
- UnregisterClass (szFormDlgClassName, hLibInstance);
- }
-
-
- BOOL FAR PASCAL LibMain (HINSTANCE hInst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine)
- {
- WNDCLASS wc;
-
- if (! hLibInstance)
- {
- hLibInstance = hInst;
- BWCCGetVersion (); // Forces BWCC to be dynamically loaded.
-
- // Register any window classes used by the form. Forms will usually
- // register either one or occasionally two classes which define
- // the composition and reader dialogs created by the DLL.
- //
- // There's a gotcha here, of course (aren't there always, in
- // Windows?)... You can't register a window class more than once,
- // so if the DLL has already been loaded and the user asks to
- // create a second instance of the form, we have to be careful
- // not to re-register the class. We do this by checking the
- // instance usage counter of the DLL - if it's greater than 1,
- // then we DON'T register the classes.
-
- #pragma warn -sig
- wc.style = WS_CHILD;
- #pragma warn .sig
- wc.lpfnWndProc = MrgProc;
- wc.cbClsExtra = 0;
- wc.cbWndExtra = DLGWINDOWEXTRA;
- wc.hInstance = hLibInstance;
- wc.hIcon = NULL;
- wc.hCursor = LoadCursor (NULL, IDC_ARROW);
- wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
- wc.lpszMenuName = NULL;
- wc.lpszClassName = szFormDlgClassName;
- if (! RegisterClass (&wc))
- MessageBeep (0);
- }
-
- return (TRUE); // Initialization went OK
- }
-
-